#include "stdafx.h"
#include "Scene.h"
#include "MeshModel.h"
#include "PrimMeshModel.h"
#include <string>
#include "camera.h"
#include "lights.h"

using namespace std;
void Scene::loadOBJModel(string fileName,bool centralize, bool shrink, vec3 mp)
{
	MeshModel *model = new MeshModel(fileName);
	models.push_back(model);
	if (centralize) model->Centralize();
	if (shrink) model->Shrink();
	model->TranslateModel(mp.x,mp.y,mp.z);
	activeModel++; // the last loaded model is the one I set active...
}

void Scene::redraw() {
	m_renderer->ClearOutBuffer();
	m_renderer->ClearZBuffer();
	this->draw();
}

void Scene::draw()
{
	m_renderer->SetProjection(this->cameras[activeCamera]->getProjectionTransform());
	//m_renderer->setCOP(cameras[activeCamera]->getPlace()); 

	for (int i=0; i< models.size(); i++) {
		MeshModel *m = (MeshModel*)models[i]; // I  really hate doing it...
		if (i==activeModel) {
			m_renderer->TakeActivePen();
		}
		else {
			m_renderer->TakeRegularPen();
		}
		m->SetRenderer(m_renderer);
		m->SetCamera(this->cameras[activeCamera]);
		m->draw();
	}

	if (this->m_drawCameras) {
		for (int i=0; i< cameras.size(); i++) {
			vec4 c_place = cameras[i]->getPlace();
			m_renderer->SetObjectMatrices(cameras[activeCamera]->getCameraTransform(),NULL);
			m_renderer->DrawPlusAtVertex(c_place);
		}
	}
	//cout<<"m_drawLights = " <<m_drawLights<<endl;
	if(this->m_drawLights){
		//cout<<"lights.size = "<<lights.size()<<endl;
		for (int i=0; i< m_lightSources.size(); i++) {
			if (i==activeLight) {
				m_renderer->TakeActivePen();
			}
			else {
				m_renderer->TakeRegularPen();
			}
			vec4 l_place = (m_lightSources[i])->getLocation();
			m_renderer->SetObjectMatrices(cameras[activeCamera]->getCameraTransform(),NULL);
			m_renderer->DrawPlusAtVertex(l_place);
		}
	}
	m_renderer->SwapBuffers();
}

void Scene::drawDemo()
{
	m_renderer->SetDemoBuffer();
	m_renderer->SwapBuffers();
}


Scene::Scene(Renderer * r):m_renderer(r),m_drawCameras(0),m_drawLights(false) {
	//set the default camera...
	this->cameras = vector<Camera*>();
	this->m_lightSources = vector<LightSource*>();
	vec3 eye(0,0,0);
	vec3 at(0,0,-1);
	vec3 up(0,1,0);
	Camera * c = new Camera();
	c->LookAt(eye,at,up);
	c->Ortho(-1,1,-1,1,1,-1);
	cameras.push_back(c);
	c = new Camera();
	c->LookAt(vec3(0,0,3),at,up);
	c->Perspective(DEG_TO_RAD(60),1,1,6);
	cameras.push_back(c);
	this->activeCamera=0;
	this->activeModel = -1; // no model loaded still...
	this->activeLight=-1;
	flagDrawNormals = false;
}

void Scene::addLightSource(LightSource * ls ) {
	this->m_lightSources.push_back(ls);
}

void Scene::TranslateActiveModel(const GLfloat & x, const GLfloat & y, const GLfloat & z) {
	if (activeModel==-1) return;
	MeshModel * model = (MeshModel*) this->models[activeModel];
	model->TranslateModel(x,y,z);
}

void Scene::NextModel() {
	this->activeModel = (this->activeModel + 1) % this->models.size();
}

void Scene::PreviousModel() {
	activeModel = (activeModel + models.size() - 1) % models.size();
}

void Scene::SpinModel(const GLfloat & x_angle, const GLfloat & y_angle, const GLfloat & z_angle) {
	if (activeModel==-1) return;
	MeshModel * model = (MeshModel*) this->models[activeModel];
	model->SpinModel(x_angle,y_angle,z_angle);
}

void Scene::RotateModelAboutX(const GLfloat & angle) {
	if (activeModel==-1) return;
	MeshModel * model = (MeshModel*) this->models[activeModel];
	model->RotateAboutX(angle);
}

void Scene::RotateLightAboutX(const GLfloat & angle){
	if (activeLight==-1) return;
	LightSource* light = this->m_lightSources[activeLight];
	light->rotateAboutX(angle);
}

void Scene::RotateLightAboutY(const GLfloat & angle){
	if (activeLight==-1) return;
	LightSource* light = this->m_lightSources[activeLight];
	light->rotateAboutY(angle);
}

void Scene::RotateLightAboutZ(const GLfloat & angle){
	if (activeLight==-1) return;
	LightSource* light = this->m_lightSources[activeLight];
	light->rotateAboutZ(angle);
}

void Scene::RotateModelAboutY(const GLfloat & angle) {
	if (activeModel==-1) return;
	MeshModel * model = (MeshModel*) this->models[activeModel];
	model->RoateteAboutY(angle);
}

void Scene::RotateModelAboutZ(const GLfloat & angle) {
	if (activeModel==-1) return;
	MeshModel * model = (MeshModel*) this->models[activeModel];
	model->RotateAboutZ(angle);
}

void Scene::FocusOnActiveModel() {
	if (activeModel == -1) return;
	Camera * curr = cameras[activeCamera];
	MeshModel * m = (MeshModel*)models[activeModel];

	vec4 origin = m->getOrigin();
	vec4 origInWorld = m->getOriginInWorld();
	
	GLfloat width, height, depth;
	vec3 origIn3D = vec3(origInWorld.x,origInWorld.y,origInWorld.z);
	GLfloat distance = length(origIn3D);

	curr->getProjectionDimensions(width,height,depth);
	// now we want a symmetric projection
	if (curr->isOrtho()) {
		// update the orthographic projection to look right at the center of the cube encompassing the object...
		curr->Ortho(-width/2, width/2,-height/2,height/2,-distance+depth/2,-distance-depth/2);
	}

	vec3 eye = curr->getEye();
	vec3 at = curr->getAt();
	vec3 up = curr->getUp();

	if ((origin.x==0) && (origin.z==0)) {
		if (origin.y==0) {
			return;
		}
		vec4 new_up;
		if (origin.y<0) {
			//we have to look down...
			new_up = curr->getCorrector() * vec4(0,0,-1,1);
		} else {
			new_up = curr->getCorrector() * vec4(0,0,1,1);
		}
		curr->LookAt(eye,origIn3D,VEC4TOVEC3(new_up));
	} 
	else {
		curr->LookAt(eye,origIn3D,up);
	}
}

void Scene::ToggleFaceNormals(){
	m_renderer->toggleFaceNormals();
}

void Scene::ToggleVertexNormals(){
	m_renderer->toggleVertexNormals();
}

void Scene::RotateCamera(const GLfloat & x, const GLfloat & y, const GLfloat & z) {
	Camera * c = cameras[activeCamera];
	c->Rotate(x,y,z);
}

void Scene::TranslateCamera(const GLfloat & x, const GLfloat & y, const GLfloat & z) {
	Camera * c = cameras[activeCamera];
	c->Move(x,y,z);
}

void Scene::TranslateLight(const GLfloat & x, const GLfloat & y, const GLfloat & z){
	if(this->activeLight == -1){return;}
	LightSource* light = m_lightSources[this->activeLight];
	light->translateLocation(x,y,z);
}

void Scene::ScaleActiveModel(const GLfloat & x, const GLfloat & y, const GLfloat & z) {
	MeshModel * m = (MeshModel*) models[activeModel];
	m->ScaleMe(x,y,z);
}

void Scene::SymScaleActiveModel(const GLfloat & r) {
	MeshModel * m = (MeshModel*) models[activeModel];
	m->ScaleMe(r,r,r);
}

void Scene::UpdateProjectionsForRatio(GLfloat ratio) {
	for (int i=0; i<cameras.size(); i++ ) {
		cameras[i]->UpdateProjection(ratio);
	}
}
void Scene::toggleBoundingBox(){
	m_renderer->toggleBoundingBox();
}

void Scene::addArk(){
	PrimMeshModel *ark = new Ark(m_renderer);
	ark->build(vec3(-0.5,-0.5,-0.5),vec3(0.5,0.5,0.5));
	models.push_back(ark);
	activeModel++; // the last loaded model is the one I set active...
	activeModel %= this->models.size();
}

void Scene::removeActiveModel(){
	if(models.size() == 0){
		return;
	}
	vector<MeshModel*>::iterator iter = models.begin();
	//printf("\nactive model = %d\n\n\n\n\n\n\n\n", activeModel);
	for(int i=0;i<activeModel;i++){
		iter++;
	}

	models.erase(iter);
	activeModel = activeModel++;
	if(models.size() != 0){
		activeModel = activeModel % models.size();
	}
	else{
		activeModel = -1;
	}
}

void Scene::removeAllModels(){
	while(models.size() != 0){
		removeActiveModel();


	}
}

Scene::~Scene() {
	for (int i=0; i< this->cameras.size(); i++) {
		Camera * c = cameras[i];
		delete c;
	}
	for (int i=0; i<this->models.size(); i++) {
		MeshModel * m = this->models[i];
		delete m;
	}
}

void Scene::MoveZFar(GLfloat len) {
	// now what we have to do is change the projection...
	Camera * c = cameras[activeCamera];
	//GLfloat left, right, top, bottom, zNear, zFar;
	c->MoveZFar(len);
}

void Scene::MoveZNear(GLfloat len) {
	Camera * c = cameras[activeCamera];
	c->MoveZNear(len);
}



vec3 Scene::SuggestMountingPoint() const {
	if (cameras.size()==0) return vec3(0,0,0);
	return cameras[activeCamera]->SuggestMountingPoint();
}

#define ROT_NORMALIZE_FACTOR 1000
#define LEAN_NORMALIZE_FACTOR (ROT_NORMALIZE_FACTOR/10)

void Scene::LeanCameraRight(){
	Camera* c = cameras[activeCamera];
	vec3 worldUp = c->getUp();
	vec3 worldAt = c->getAt();
	vec3 worldEye = c->getEye();
	vec3 worldEyeUp = worldUp - worldEye;
		while (length(worldEyeUp)<0.005f){//fight numeric error
			worldUp += worldEyeUp;
			worldEyeUp = worldUp - worldEye;
		}
	vec3 worldEyeAt = worldAt - worldEye;
		while (length(worldEyeAt)<0.005f){//fight numeric error
			worldAt += worldEyeAt;
			worldEyeAt = worldAt - worldEye;
		}
	vec3 right = cross(worldEyeAt,worldEyeUp);
	right = normalize(right);
	right = right / LEAN_NORMALIZE_FACTOR;
	GLfloat upSize = length(worldUp);
	worldUp = worldUp+right;
	worldUp = normalize(worldUp);
	worldUp = worldUp * upSize;
	c->LookAt(worldEye,worldAt,worldUp);
}

void Scene::LeanCameraLeft(){
	Camera* c = cameras[activeCamera];
	vec3 worldUp = c->getUp();
	vec3 worldAt = c->getAt();
	vec3 worldEye = c->getEye();
	vec3 worldEyeUp = worldUp - worldEye;
		while (length(worldEyeUp)<0.005f){//fight numeric error
			worldUp += worldEyeUp;
			worldEyeUp = worldUp - worldEye;
		}
	vec3 worldEyeAt = worldAt - worldEye;
		while (length(worldEyeAt)<0.005f){//fight numeric error
			worldAt += worldEyeAt;
			worldEyeAt = worldAt - worldEye;
		}
	vec3 right = cross(worldEyeAt,worldEyeUp);
	right = normalize(right);
	right = right / LEAN_NORMALIZE_FACTOR;
	GLfloat upSize = length(worldUp);
	worldUp = worldUp-right;
	worldUp = normalize(worldUp);
	worldUp = worldUp * upSize;
	c->LookAt(worldEye,worldAt,worldUp);
}

#define ROTATE_NORMALIZE_FACTOR 1000

void Scene::RotateCameraUp(){

}

void Scene::RotateCameraDown(){

}

void Scene::RotateCameraRight(){

}

void Scene::RotateCameraLeft(){

}

#define TRANS_NORMALIZE_FACTOR 200
#define TRANS_STEP_FORWARD (0.02f)
#define TRANS_STEP_RIGHT (0.005f)
#define TRANS_STEP_UP (0.005f)

void Scene::TranslateCameraForward(){
	Camera* c = cameras[activeCamera];
	//vec3 worldAt = c->getAt();
	//cout<<"worldAt is :"<<worldAt <<endl;
	//vec3 worldEye = c->getEye();
	//cout<<"worldEye is :"<<worldEye <<endl;
	//vec3 worldMoveToward = (worldAt - worldEye);//numeric cancelation here. solution: when size of this vector gets close to 0, add to worldAt this vector * (1000). this will get at far.
	//cout<<"worldMoveToward is :"<<worldMoveToward <<endl;
	//worldMoveToward = normalize(worldMoveToward);
	//cout<<"normalized worldMoveToward is :"<<worldMoveToward <<endl<<endl;
	//worldMoveToward = worldMoveToward / TRANS_NORMALIZE_FACTOR;
	//c->LookAt(worldEye+worldMoveToward,worldAt,c->getUp());
	c->Move(0,0,-TRANS_STEP_FORWARD);
}

void Scene::TranslateCameraBack(){
	Camera* c = cameras[activeCamera];
	//vec3 worldAt = c->getAt();
	////cout<<"worldAt is :"<<worldAt <<endl;
	//vec3 worldEye = c->getEye();
	////cout<<"worldEye is :"<<worldEye <<endl;
	//vec3 worldMoveToward = (worldAt - worldEye);
	//worldMoveToward = normalize(worldMoveToward);
	////cout<<"worldMoveToward is :"<<worldMoveToward <<endl;
	//worldMoveToward = worldMoveToward / TRANS_NORMALIZE_FACTOR;
	//c->LookAt(worldEye-worldMoveToward,worldAt,c->getUp());
	c->Move(0,0,TRANS_STEP_FORWARD);
}

void Scene::TranslateCameraUp(){
	Camera* c = cameras[activeCamera];
	//vec3 worldUp = c->getUp();
	////cout<<"worldUp is :"<<worldUp <<endl;
	//vec3 worldAt = c->getAt();
	////cout<<"worldAt is :"<<worldAt <<endl;
	//vec3 worldEye = c->getEye();
	////cout<<"worldEye is :"<<worldEye <<endl;
	//vec3 worldMoveToward = (worldUp - worldEye);
	//worldMoveToward = normalize(worldMoveToward);
	////cout<<"worldMoveToward is :"<<worldMoveToward <<endl;
	//worldMoveToward = worldMoveToward / TRANS_NORMALIZE_FACTOR;
	//c->LookAt(worldEye+worldMoveToward,worldAt+worldMoveToward,worldUp);
	c->Move(0,-TRANS_STEP_UP,0);
}

void Scene::TranslateCameraDown(){
	Camera* c = cameras[activeCamera];
	//vec3 worldUp = c->getUp();
	////cout<<"worldUp is :"<<worldUp <<endl;
	//vec3 worldAt = c->getAt();
	////cout<<"worldAt is :"<<worldAt <<endl;
	//vec3 worldEye = c->getEye();
	////cout<<"worldEye is :"<<worldEye <<endl;
	//vec3 worldMoveToward = (worldUp - worldEye);
	//worldMoveToward = normalize(worldMoveToward);
	////cout<<"worldMoveToward is :"<<worldMoveToward <<endl;
	//worldMoveToward = worldMoveToward / TRANS_NORMALIZE_FACTOR;
	//c->LookAt(worldEye-worldMoveToward,worldAt-worldMoveToward,worldUp);
	c->Move(0,TRANS_STEP_UP,0);
}

void Scene::TranslateCameraRight(){
	Camera* c = cameras[activeCamera];
	//vec3 worldUp = c->getUp();
	////cout<<"worldUp is :"<<worldUp <<endl;
	//vec3 worldAt = c->getAt();
	////cout<<"worldAt is :"<<worldAt <<endl;
	//vec3 worldEye = c->getEye();
	////cout<<"worldEye is :"<<worldEye <<endl;
	//vec3 worldEyeUp = worldUp - worldEye;
	//vec3 worldEyeAt = worldAt - worldEye;
	//vec3 worldMoveToward = cross(worldEyeAt,worldEyeUp);
	//worldMoveToward = normalize(worldMoveToward);
	////cout<<"worldMoveToward is :"<<worldMoveToward <<endl;
	//worldMoveToward = worldMoveToward / TRANS_NORMALIZE_FACTOR;
	//c->LookAt(worldEye+worldMoveToward,worldAt+worldMoveToward,worldUp);
	c->Move(-TRANS_STEP_RIGHT,0,0);
}

void Scene::TranslateCameraLeft(){
	Camera* c = cameras[activeCamera];
	//vec3 worldUp = c->getUp();
	////cout<<"worldUp is :"<<worldUp <<endl;
	//vec3 worldAt = c->getAt();
	////cout<<"worldAt is :"<<worldAt <<endl;
	//vec3 worldEye = c->getEye();
	////cout<<"worldEye is :"<<worldEye <<endl;
	//vec3 worldEyeUp = worldUp - worldEye;
	//vec3 worldEyeAt = worldAt - worldEye;
	//vec3 worldMoveToward = cross(worldEyeAt,worldEyeUp);
	//worldMoveToward = normalize(worldMoveToward);
	////cout<<"worldMoveToward is :"<<worldMoveToward <<endl;
	//worldMoveToward = worldMoveToward / TRANS_NORMALIZE_FACTOR;
	//c->LookAt(worldEye-worldMoveToward,worldAt-worldMoveToward,worldUp);
	c->Move(TRANS_STEP_RIGHT,0,0);
}
